package com.cmeduSystem.common.flow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.cmeduSystem.common.core.annotation.MyDataSourceResolver;
import com.cmeduSystem.common.core.base.dao.BaseDaoMapper;
import com.cmeduSystem.common.core.base.service.BaseService;
import com.cmeduSystem.common.core.constant.ApplicationConstant;
import com.cmeduSystem.common.core.object.TokenData;
import com.cmeduSystem.common.core.util.DefaultDataSourceResolver;
import com.cmeduSystem.common.flow.dao.FlowDelegateUserMapper;
import com.cmeduSystem.common.flow.dao.FlowDelegatedUserMapper;
import com.cmeduSystem.common.flow.model.FlowDelegateUser;
import com.cmeduSystem.common.flow.model.FlowDelegatedUser;
import com.cmeduSystem.common.flow.model.constant.FlowDelegatedUserConfirmStatus;
import com.cmeduSystem.common.flow.service.FlowDelegateUserService;
import com.cmeduSystem.common.sequence.wrapper.IdGeneratorWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@MyDataSourceResolver(
        resolver = DefaultDataSourceResolver.class,
        intArg = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowDelegateUserService")
public class FlowDelegateUserServiceImpl extends BaseService<FlowDelegateUser, Long> implements FlowDelegateUserService {

    @Autowired
    private FlowDelegateUserMapper flowDelegateUserMapper;
    @Autowired
    private FlowDelegatedUserMapper flowDelegatedUserMapper;
    @Autowired
    private IdGeneratorWrapper idGenerator;

    /**
     * 返回当前Service的主表Mapper对象。
     *
     * @return 主表Mapper对象。
     */
    @Override
    protected BaseDaoMapper<FlowDelegateUser> mapper() {
        return flowDelegateUserMapper;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public FlowDelegateUser saveNew(FlowDelegateUser flowDelegateUser) {
        flowDelegateUserMapper.insert(this.buildDefaultValue(flowDelegateUser));
        StrUtil.split(flowDelegateUser.getDelegateUsers(), StrUtil.COMMA).forEach(
                userId -> this.saveNewDelegatedUser(flowDelegateUser.getId(), userId));
        return flowDelegateUser;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean update(FlowDelegateUser flowDelegateUser, FlowDelegateUser originalFlowDelegateUser) {
        if (!StrUtil.equals(flowDelegateUser.getDelegateUsers(), originalFlowDelegateUser.getDelegateUsers())) {
            Set<String> originalUserIds =
                    new HashSet<>(StrUtil.split(originalFlowDelegateUser.getDelegateUsers(), StrUtil.COMMA));
            Set<String> userIds =
                    new HashSet<>(StrUtil.split(flowDelegateUser.getDelegateUsers(), StrUtil.COMMA));
            for (String userId : userIds) {
                if (!originalUserIds.contains(userId)) {
                    this.saveNewDelegatedUser(flowDelegateUser.getId(), userId);
                }
            }
            for (String originalUserId : originalUserIds) {
                if (!userIds.contains(originalUserId)) {
                    LambdaQueryWrapper<FlowDelegatedUser> qw = new LambdaQueryWrapper<>();
                    qw.eq(FlowDelegatedUser::getDelegateId, flowDelegateUser.getId());
                    qw.eq(FlowDelegatedUser::getUserId, Long.valueOf(originalUserId));
                    flowDelegatedUserMapper.delete(qw);
                }
            }
        }
        flowDelegateUser.setCreateUserId(originalFlowDelegateUser.getCreateUserId());
        flowDelegateUser.setCreateLoginName(originalFlowDelegateUser.getCreateLoginName());
        flowDelegateUser.setCreateShowName(originalFlowDelegateUser.getCreateShowName());
        flowDelegateUser.setUpdateUserId(TokenData.takeFromRequest().getUserId());
        flowDelegateUser.setCreateTime(originalFlowDelegateUser.getCreateTime());
        flowDelegateUser.setUpdateTime(new Date());
        // 这里重点提示，在执行主表数据更新之前，如果有哪些字段不支持修改操作，请用原有数据对象字段替换当前数据字段。
        UpdateWrapper<FlowDelegateUser> uw = this.createUpdateQueryForNullValue(flowDelegateUser, flowDelegateUser.getId());
        return flowDelegateUserMapper.update(flowDelegateUser, uw) == 1;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean remove(Long id) {
        flowDelegatedUserMapper.delete(new LambdaQueryWrapper<FlowDelegatedUser>().eq(FlowDelegatedUser::getDelegateId, id));
        return flowDelegateUserMapper.deleteById(id) == 1;
    }

    @Override
    public List<FlowDelegateUser> getFlowDelegateUserList(String orderBy) {
        FlowDelegateUser filter = new FlowDelegateUser();
        filter.setCreateUserId(TokenData.takeFromRequest().getUserId());
        List<FlowDelegateUser> dataList = flowDelegateUserMapper.getFlowDelegateUserList(filter, orderBy);
        if (CollUtil.isNotEmpty(dataList)) {
            List<Long> delegatedIds = dataList.stream().map(FlowDelegateUser::getId).collect(Collectors.toList());
            LambdaQueryWrapper<FlowDelegatedUser> qw = new LambdaQueryWrapper<>();
            qw.in(FlowDelegatedUser::getDelegateId, delegatedIds);
            qw.orderByAsc(FlowDelegatedUser::getId);
            List<FlowDelegatedUser> delegatedUsers = flowDelegatedUserMapper.selectList(qw);
            Map<Long, List<FlowDelegatedUser> > delegatedUserMap = delegatedUsers.stream()
                    .collect(Collectors.groupingBy(FlowDelegatedUser::getDelegateId));
            for (FlowDelegateUser data : dataList) {
                data.setFlowDelegatedUserList(delegatedUserMap.get(data.getId()));
            }
        }
        return dataList;
    }

    @Override
    public void updateDeletegatedUserConfirm(Long delegateId, Integer confirmStatus) {
        flowDelegatedUserMapper.updateConfirmStatus(delegateId, TokenData.takeFromRequest().getUserId(), confirmStatus);
    }

    @Override
    public List<FlowDelegateUser> getFlowDelegateUserListByMyUserId(String processDefinitionKey, boolean effective) {
        List<FlowDelegateUser> delegateUsers =
                flowDelegateUserMapper.getFlowDelegateUserListByMyUserId(TokenData.takeFromRequest().getUserId(), effective);
        if (StrUtil.isNotBlank(processDefinitionKey)) {
            delegateUsers = delegateUsers.stream()
                    .filter(d -> StrUtil.isBlank(d.getProcessDefinitionKeys())
                            || StrUtil.split(d.getProcessDefinitionKeys(), StrUtil.COMMA).contains(processDefinitionKey))
                    .collect(Collectors.toList());
        }
        return delegateUsers;
    }

    @Override
    public boolean isEffectiveDelegate(String processDefinitionKey, Long delegateUserId) {
        List<FlowDelegateUser> delegateUsers = this.getFlowDelegateUserListByMyUserId(processDefinitionKey, true);
        return delegateUsers.stream().anyMatch(d -> d.getCreateUserId().equals(delegateUserId));
    }

    private FlowDelegateUser buildDefaultValue(FlowDelegateUser flowDelegateUser) {
        flowDelegateUser.setId(idGenerator.nextLongId());
        TokenData tokenData = TokenData.takeFromRequest();
        flowDelegateUser.setCreateUserId(tokenData.getUserId());
        flowDelegateUser.setUpdateUserId(tokenData.getUserId());
        flowDelegateUser.setCreateLoginName(tokenData.getLoginName());
        flowDelegateUser.setCreateShowName(tokenData.getShowName());
        Date now = new Date();
        flowDelegateUser.setCreateTime(now);
        flowDelegateUser.setUpdateTime(now);
        return flowDelegateUser;
    }

    private void saveNewDelegatedUser(Long delegatedId, String userId) {
        FlowDelegatedUser delegatedUser = new FlowDelegatedUser();
        delegatedUser.setId(idGenerator.nextLongId());
        delegatedUser.setUserId(Long.valueOf(userId));
        delegatedUser.setDelegateId(delegatedId);
        delegatedUser.setConfirmStatus(FlowDelegatedUserConfirmStatus.NO_CONFIRM);
        flowDelegatedUserMapper.insert(delegatedUser);
    }
}